package com.gitee.dolt.boot.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.gitee.dolt.core.cache.model.CacheKey;
import com.gitee.dolt.core.cache.model.CacheKeyBuilder;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.springframework.lang.NonNull;

import java.io.Serializable;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Accessors(chain = true)
@RequiredArgsConstructor
public class ServiceCacheManager<T> extends AbstractServiceCacheManager<T> {

    private static final int MAX_BATCH_KEY_SIZE = 20;

    private final CacheKeyBuilder cacheKeyBuilder;
    private final Function<T, Object> modelToKey;

    @Setter
    private Function<T, Object> modelToValue = t -> t;

    @Override
    public void setCache(@NonNull T model) {
        Object key = modelToKey.apply(model);
        if (key != null) {
            CacheKey cacheKey = cacheKeyBuilder.key(key);
            cachePlusOps.set(cacheKey, modelToValue.apply(model));
        }
    }

    @Override
    public void delCache(@NonNull T model) {
        Optional.ofNullable(model).map(modelToKey)
                .filter(e -> !StrUtil.isEmptyIfStr(e)).map(cacheKeyBuilder::key).ifPresent(cachePlusOps::del);
    }

    @Override
    public void delCache(@NonNull List<T> models) {
        CacheKey[] cacheKeys = models.stream().map(modelToKey)
                .filter(e -> !StrUtil.isEmptyIfStr(e)).map(cacheKeyBuilder::key).toArray(CacheKey[]::new);
        cachePlusOps.del(cacheKeys);
    }

    /**
     * 查找 找不到的键会调用loader 为空的会去查数据库 不存在缓存空
     *
     * @param keys
     * @param loader
     * @return
     */
    @Override
    public List<T> findByKeys(@NonNull Collection<? extends Serializable> keys, Function<Collection<? extends Serializable>, Collection<T>> loader) {
        if (keys.isEmpty()) {
            return Collections.emptyList();
        }
        // 拼接keys
        List<CacheKey> cacheKeys = keys.stream().map(cacheKeyBuilder::key).collect(Collectors.toList());
        // 切割
        List<List<CacheKey>> partitionKeys = CollUtil.split(cacheKeys, MAX_BATCH_KEY_SIZE);

        // 用切割后的 partitionKeys 分批去缓存查， 返回的是缓存中存在的数据
        List<T> valueList = partitionKeys.stream().map(ks -> (List<T>) cachePlusOps.find(ks)).flatMap(Collection::stream).collect(Collectors.toList());

        // 所有的key
        List<? extends Serializable> keysList = CollUtil.newArrayList(keys);
        // 缓存不存在的key
        Set<Serializable> missedKeys = CollUtil.newLinkedHashSet();

        List<T> allList = new ArrayList<>();
        for (int i = 0; i < valueList.size(); i++) {
            T v = valueList.get(i);
            Serializable k = keysList.get(i);
            if (v == null) {
                missedKeys.add(k);
            } else {
                allList.add(v);
            }
        }
        // 加载miss 的数据，并设置到缓存
        if (loader != null && CollUtil.isNotEmpty(missedKeys)) {
            Collection<T> missList = loader.apply(missedKeys);
            missList.forEach(this::setCache);
            allList.addAll(missList);
        }
        return allList;
    }

    /**
     * 查找 找不到的键会调用loader 空也可能被缓存
     *
     * @param key
     * @return
     */
    @Override
    public T getByKey(@NonNull Serializable key, @NonNull Function<Serializable, T> loader) {
        CacheKey cacheKey = cacheKeyBuilder.key(key);
        T value = cachePlusOps.get(cacheKey, e -> loader.apply(key));
        return value;
    }


}
